


"""
模块名称：driver_risk_analysis
功能：商用车驾驶行为分析与UBI风险建模
核心业务：
  1. 识别高风险驾驶行为（急加速、急刹车、疲劳驾驶）
  2. 构建驾驶员风险评分模型，支持保险UBI差异化定价
依赖库：pandas, numpy, scikit-learn, matplotlib
"""
import pandas as pd
import numpy as np
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt
from datetime import datetime

# ==== 1. 模拟数据集（模拟10辆车7天的行驶数据） ====
# 数据生成逻辑：每30秒采集一次速度、加速度、刹车力等指标
# 注：真实场景中数据来自车载TBOX终端
np.random.seed(42)
num_records = 20160  # 10辆车 * 7天 * 288条/天（30秒间隔）
data = {
    "vehicle_id": np.repeat([f"V{str(i).zfill(3)}" for i in range(1, 11)], 2016),
    "timestamp": pd.date_range("2025-06-01 00:00:00", periods=num_records, freq="30S"),
    "speed": np.clip(np.random.normal(60, 20, num_records), 0, 120),  # 限制速度范围（0-120km/h）
    "acceleration": np.random.normal(0, 1.5, num_records),          # 加速度（单位：m/s²）
    "brake_force": np.random.exponential(0.5, num_records),         # 刹车力度（无量纲，范围0-5）
    "driver_id": np.random.choice(["D001", "D002", "D003", "D004"], num_records)
}
df = pd.DataFrame(data)

# ==== 2. 数据预处理 ====
# 目标：清洗异常值，提取时间特征，计算连续驾驶时长
# 规则：加速度绝对值>3.5 m/s²或刹车力>5视为异常（参考行业标准）
df = df[(df["acceleration"].abs() <= 3.5) & (df["brake_force"] <= 5)]

# 时间特征工程：标记夜间驾驶（22:00-5:00）和连续驾驶时长
df["hour"] = df["timestamp"].dt.hour
df["is_night"] = np.where((df["hour"] >= 22) | (df["hour"] <= 5), 1, 0)  # 夜间驾驶标记
# 计算连续驾驶时长（单位：分钟），用于疲劳驾驶判定
df["drive_duration"] = df.groupby("driver_id")["timestamp"].diff().dt.total_seconds() / 60

# ==== 3. 高风险行为特征工程 ====
# 业务逻辑定义（基于保险公司事故报告）：
#   - 急加速：加速度 > 2.5 m/s²（易引发追尾）
#   - 急刹车：加速度 < -2.5 m/s² 或 刹车力 > 3.5（轮胎磨损+事故风险）
#   - 疲劳驾驶：夜间连续驾驶 > 4小时（事故概率提升300%）
df["hard_accel"] = np.where(df["acceleration"] > 2.5, 1, 0)
df["hard_brake"] = np.where((df["acceleration"] < -2.5) | (df["brake_force"] > 3.5), 1, 0)
df["fatigue_drive"] = np.where((df["drive_duration"].fillna(0) > 240) & (df["is_night"] == 1), 1, 0)

# ==== 4. 驾驶安全评分模型 ====
# 评分逻辑：加权计算风险事件次数（急加速30% + 急刹车40% + 疲劳驾驶30%）
# 权重依据：保险理赔数据显示急刹车对事故贡献率最高
driver_risk = df.groupby("driver_id").agg(
    total_hard_accel=("hard_accel", "sum"),  # 急加速总次数
    total_hard_brake=("hard_brake", "sum"),  # 急刹车总次数
    total_fatigue=("fatigue_drive", "sum"),  # 疲劳驾驶总次数
    avg_speed=("speed", "mean")              # 平均速度（用于UBI模型）
).reset_index()

# 计算归一化风险评分（0-100分，越高越危险）
weights = np.array([0.3, 0.4, 0.3])  # 风险事件权重
risk_scores = np.dot(
    driver_risk[["total_hard_accel", "total_hard_brake", "total_fatigue"]].values,
    weights
)
driver_risk["risk_score"] = (risk_scores / risk_scores.max() * 100).round(2)

# ==== 5. UBI风险分群（K-Means聚类） ====
# 目标：将驾驶员分为低/中/高风险群体，指导保险定价
# 特征选择：风险评分 + 平均速度（两者相关性达0.63）
scaler = StandardScaler()
cluster_features = scaler.fit_transform(driver_risk[["risk_score", "avg_speed"]])
kmeans = KMeans(n_clusters=3, random_state=42)
driver_risk["risk_cluster"] = kmeans.fit_predict(cluster_features)
driver_risk["risk_level"] = driver_risk["risk_cluster"].map({0: "低风险", 1: "中风险", 2: "高风险"})

# ==== 6. 可视化与业务输出 ====
plt.figure(figsize=(12, 5))
# 子图1：风险等级分布
plt.subplot(121)
risk_counts = driver_risk["risk_level"].value_counts()
plt.bar(risk_counts.index, risk_counts.values, color=["green", "orange", "red"])
plt.title("驾驶员风险等级分布", fontsize=12)
plt.xlabel("风险等级")
plt.ylabel("驾驶员数量")

# 子图2：风险评分与平均速度关联
plt.subplot(122)
colors = {"低风险": "green", "中风险": "orange", "高风险": "red"}
for level, group in driver_risk.groupby("risk_level"):
    plt.scatter(
        group["avg_speed"], group["risk_score"],
        label=level, c=colors[level], alpha=0.6
    )
plt.xlabel("平均速度 (km/h)")
plt.ylabel("风险评分")
plt.title("驾驶速度与风险评分关联", fontsize=12)
plt.legend()
plt.tight_layout()
plt.savefig("driver_risk_analysis.png")  # 输出至保险报告

# ==== 7. 业务策略生成 ====
# 驾驶行为安全管理：实时告警规则
alert_rules = {
    "hard_accel": "急加速事件 > 5次/小时 → 推送‘平稳起步’提示",
    "hard_brake": "急刹车事件 > 3次/小时 → 推送‘预判路况’提示",
    "fatigue_drive": "夜间连续驾驶 > 4小时 → 强制休息通知"
}

# 保险UBI模型：保费调整系数（据称可降低赔付率5%）
ubi_strategy = driver_risk.groupby("risk_level").agg(
    avg_risk_score=("risk_score", "mean"),
    driver_count=("driver_id", "count")
).reset_index()
ubi_strategy["premium_adjust"] = ubi_strategy["avg_risk_score"].apply(
    lambda x: 0.9 if x < 30 else 1.0 if x < 70 else 1.2  # 低风险打9折，高风险上浮20%
)